package com.lry.table;

import com.lry.block.Block;
import com.lry.block.BlockHandle;
import com.lry.util.Slice;
import com.lry.util.SliceInput;
import com.lry.util.SliceOutput;
import com.lry.util.Slices;

import static com.google.common.base.Preconditions.checkArgument;
import static com.lry.block.BlockHandle.readBlockHandle;
import static com.lry.block.BlockHandle.writeBlockHandleTo;
import static com.lry.util.SizeOf.SIZE_OF_LONG;
import static java.util.Objects.requireNonNull;

public class Footer {

    public static final int ENCODED_LENGTH = (BlockHandle.MAX_ENCODED_LENGTH * 2) + SIZE_OF_LONG;

    private final BlockHandle metaIndexBlockHandle;
    private final BlockHandle indexBlockHandle;

    public Footer(BlockHandle metaIndexBlockHandle, BlockHandle indexBlockHandle) {
        this.metaIndexBlockHandle = metaIndexBlockHandle;
        this.indexBlockHandle = indexBlockHandle;
    }

    public BlockHandle getMetaIndexBlockHandle() {
        return metaIndexBlockHandle;
    }

    public BlockHandle getIndexBlockHandle() {
        return indexBlockHandle;
    }

    public static Footer readFooter(Slice slice){
        requireNonNull(slice, "slice is null");
        checkArgument(slice.length() == ENCODED_LENGTH, "Expected slice.size to be %s but was %s", ENCODED_LENGTH, slice.length());

        SliceInput sliceInput = slice.input();

        BlockHandle metaIndexBlockHandle = readBlockHandle(sliceInput);
        BlockHandle indexBlockHandle = readBlockHandle(sliceInput);

        // skip footer padding
        sliceInput.setPosition(ENCODED_LENGTH - SIZE_OF_LONG);

        // verify magic number
        long magicNumber = sliceInput.readUnsignedInt() | (sliceInput.readUnsignedInt() << 32);
        checkArgument(magicNumber == TableBuilder.TABLE_MAGIC_NUMBER, "File is not a table (bad magic number)");

        return new Footer(metaIndexBlockHandle, indexBlockHandle);
    }

    public static Slice writeFooter(Footer footer){
        Slice slice = Slices.allocate(ENCODED_LENGTH);
        writeFooter(footer,slice.output());
        return slice;
    }

    private static void writeFooter(Footer footer, SliceOutput output) {
        // remember the starting write index so we can calculate the padding
        int startingWriteIndex = output.size();

        // write metaindex and index handles
        writeBlockHandleTo(footer.getMetaIndexBlockHandle(), output);
        writeBlockHandleTo(footer.getIndexBlockHandle(), output);

        // write padding
        output.writeZero(ENCODED_LENGTH - SIZE_OF_LONG - (output.size() - startingWriteIndex));

        // write output number as two (little endian) integers
        output.writeInt((int) TableBuilder.TABLE_MAGIC_NUMBER);
        output.writeInt((int) (TableBuilder.TABLE_MAGIC_NUMBER >>> 32));
    }
}
